package mybatisplus.spring.boot.admin.common.util;

import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * Redisson实现的分布式锁
 *
 * @author yichuang.chen
 */
@Component
@Slf4j
public class RedisLock {
    private ThreadLocal<RLock> threadLocal = new ThreadLocal<>();

    @Autowired
    private RedissonClient redissonClient;

    /**
     * 每个key名称拼上前缀，防止误传业务key，本锁机制会对key赋值
     */
    public static final String PREFIX = "redis_lock:";

    /**
     * 默认获取锁的最大等待时间
     */
    public static int TIME_OUT = 10;

    /**
     * 获取锁，默认最大等待10秒后超时，获取到锁后10秒后过期
     *
     * @param key
     * @return
     */
    public boolean lock(String key) {
        return lock(key, TIME_OUT);
    }

    /**
     * 获取锁，默认10秒后超时
     *
     * @param key
     * @param expire 锁过期时间
     * @return
     */
    public boolean lock(String key, int expire) {
        return lock(key, expire, TIME_OUT);
    }

    /**
     * 获取锁，指定锁过期时间和获取锁的最大等待超时时间
     *
     * @param key
     * @param expire  锁过期时间
     * @param timeout 最大等待超时时间
     * @return
     */
    public boolean lock(String key, int expire, int timeout) {
        if (!key.startsWith(PREFIX)) {
            key = PREFIX + key;
        }

        RLock lock = redissonClient.getLock(key);
        threadLocal.set(lock);

        boolean getLock;
        try {
            long startTime = System.currentTimeMillis();
            getLock = lock.tryLock(timeout, expire, TimeUnit.SECONDS);
            log.info("[Redis-tryLock]key={},result={},time={}ms", key, getLock, System.currentTimeMillis() - startTime);
        } catch (Exception e) {
            log.error("获取分布式锁时发生异常：", e);
            threadLocal.remove();
            throw new RuntimeException(e);
        }
        if (!getLock) {
            threadLocal.remove();
            log.info("[Redis-lock未获取到分布式锁]key={}", lock.getName());
        }
        return getLock;
    }

    /**
     * 释放锁
     */
    public void unlock() {
        RLock lock = threadLocal.get();
        if (lock != null) {
            try {
                lock.unlock();
                log.info("[Redis-unLock]key={}", lock.getName());
            } catch (Exception e) {
                log.error("释放分布式锁时发生异常：", e);
            } finally {
                threadLocal.remove();
            }
        }
    }

}
